package mrusanov.view

import java.awt.{Graphics2D, Point}
import java.awt.geom.Point2D
import mrusanov.images.Image
import mrusanov.sounds.Sound

class MovingSpriteEffect(images:Map[MovingSpriteEffectStage, Vector[Image]], distance:Double, endMoveSound:Option[Sound],
                         pixelsPerSecond:Double = 0.5) extends GraphicEffect {
  private var stage:MovingSpriteEffectStage = StartMove
  private val sprites = images map(kv =>
    (kv._1 -> new AnimatedSprite(kv._2, cycle = kv._1 == Move)))

  private var movement:LineMovement = _
  private var startPoint:Point = _
  private var endPoint:Point = _
  private def currentSprite:Sprite = sprites(stage)

  def initEffect(start:Point, end:Point) {
    movement = new LineMovement(start, end, distance, pixelsPerSecond)
    startPoint = new Point(start)
    endPoint = new Point(end)
  }

  def reset() {
    stage = StartMove
    movement.reset()
  }

  def update(time:Int) {
    currentSprite.update(time)

    stage match {
      case StartMove =>
        if (currentSprite.isOver) {
            stage = Move
        }
      case Move => movement.update(time)
        if (movement.over) {
          stage = EndMove
          endMoveSound foreach(_.play())
        }
      case EndMove =>
        if (currentSprite.isOver) {
          stage = Finished
        }
      case Finished => throw new IllegalStateException("Effect is finished!")
      case _ => throw new IllegalArgumentException("Unreachable code")
    }
  }

  def render(g:Graphics2D) {
    stage match {
      case StartMove => currentSprite.render(g, startPoint.x, startPoint.y)
      case Move => currentSprite.render(g, movement.currentPoint.getX.toInt, movement.currentPoint.getY.toInt)
      case EndMove => currentSprite.render(g, endPoint.x, endPoint.y)
      case _ => throw new IllegalArgumentException("Unreachable code")
    }

  }

  def over:Boolean = stage == Finished


}

class LineMovement(start:Point, end:Point, distancePercentage:Double, pixelPerSecondSpeed:Double) {
  private val _currentPoint = new Point2D.Double(start.x, start.y)
  private val distance = start.distance(end)
  private var distanceMoved = 0d

  private val normalizedVector = normalize(new Point(end.x - start.x, end.y - start.y))

  def update(time:Int) {
    val movement = pixelPerSecondSpeed * 1000 / time
    distanceMoved += movement
    _currentPoint.x += normalizedVector.getX * movement
    _currentPoint.y += normalizedVector.getY * movement
  }

  def normalize(p:Point):Point2D = {
    val sum = scala.math.sqrt(p.x * p.x + p.y * p.y)
    new Point2D.Double(p.x / sum, p.y / sum)
  }

  def currentPoint = new Point2D.Double(_currentPoint.getX, _currentPoint.getY)
  def over = distanceMoved >= distance * distancePercentage

  def reset() {
    distanceMoved = 0
  }
}

sealed class MovingSpriteEffectStage(val name:String)
object StartMove extends MovingSpriteEffectStage("start")
object Move extends MovingSpriteEffectStage("move")
object EndMove extends MovingSpriteEffectStage("end")
object Finished extends MovingSpriteEffectStage(null)
